home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Numbers / Roman.php
PHP Script  |  2004-10-01  |  10KB  |  276 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors :  David Costa <gurugeek@php.net>                            |
  17. // |            Sterling Hughes <sterling@php.net>                        |
  18. // +----------------------------------------------------------------------+
  19. // $Id: Roman.php,v 1.14 2004/04/28 13:13:08 danielc Exp $
  20.  
  21. // {{{ Numbers_Roman
  22.  
  23. /**
  24.  * Provides utilities to convert roman numerals to
  25.  * arabic numbers and convert arabic numbers to roman numerals.
  26.  *
  27.  * Supports lower case input and output and some furthers conversion
  28.  * functions.
  29.  *
  30.  * @access public
  31.  * @author David Costa <gurugeek@php.net>
  32.  * @author Sterling Hughes <sterling@php.net>
  33.  * @package Numbers_Roman
  34.  */
  35. class Numbers_Roman
  36. {
  37.     // {{{ toNumber()
  38.  
  39.     /**
  40.      * Converts a roman numeral to a number
  41.      *
  42.      * @param  string  $roman The roman numeral to convert
  43.      *                        lower cased numerals are converted into
  44.      *                        uppercase
  45.      * @return integer $num   The number corresponding to the
  46.      *                        given roman numeral
  47.      * @access public
  48.      */
  49.     function toNumber($roman)
  50.     {
  51.         $roman = strtoupper($roman);
  52.  
  53.         /*
  54.          * Replacing the Numerals representing an integer higher then 4000
  55.          * e.g. _X represent 10 000 _L  represent 50 000 etc
  56.          * we first convert them into single characters
  57.          */
  58.         $roman = str_replace('_V', 'S', $roman);
  59.         $roman = str_replace('_X', 'R', $roman);
  60.         $roman = str_replace('_L', 'Q', $roman);
  61.         $roman = str_replace('_C', 'P', $roman);
  62.         $roman = str_replace('_D', 'O', $roman);
  63.         $roman = str_replace('_M', 'N', $roman);
  64.  
  65.         $conv = array(
  66.             array('letter' => 'I', 'number' => 1),
  67.             array('letter' => 'V', 'number' => 5),
  68.             array('letter' => 'X', 'number' => 10),
  69.             array('letter' => 'L', 'number' => 50),
  70.             array('letter' => 'C', 'number' => 100),
  71.             array('letter' => 'D', 'number' => 500),
  72.             array('letter' => 'M', 'number' => 1000),
  73.             array('letter' => 'S', 'number' => 5000),
  74.             array('letter' => 'R', 'number' => 10000),
  75.             array('letter' => 'Q', 'number' => 50000),
  76.             array('letter' => 'P', 'number' => 100000),
  77.             array('letter' => 'O', 'number' => 500000),
  78.             array('letter' => 'N', 'number' => 1000000),
  79.             array('letter' => 0, 'number' => 0)
  80.         );
  81.  
  82.         $arabic = 0;
  83.         $state = 0;
  84.         $sidx = 0;
  85.         $len = strlen($roman) - 1;
  86.  
  87.         while ($len >= 0) {
  88.             $i = 0;
  89.             $sidx = $len;
  90.  
  91.             while ($conv[$i]['number'] > 0) {
  92.                 if (strtoupper($roman[$sidx]) == $conv[$i]['letter']) {
  93.                     if ($state > $conv[$i]['number']) {
  94.                         $arabic -= $conv[$i]['number'];
  95.                     } else {
  96.                         $arabic += $conv[$i]['number'];
  97.                         $state = $conv[$i]['number'];
  98.                     }
  99.                 }
  100.                 $i++;
  101.             }
  102.             $len--;
  103.         }
  104.  
  105.         return $arabic;
  106.     }
  107.  
  108.     // }}}
  109.     // {{{ toRoman()
  110.  
  111.     /**
  112.      * A backwards compatibility alias for toNumeral()
  113.      *
  114.      * @access private
  115.      */
  116.     function toRoman($num, $uppercase = true)
  117.     {
  118.         return $this->toNumeral($num, $uppercase);
  119.     }
  120.  
  121.     // }}}
  122.     // {{{ toNumeral()
  123.  
  124.     /**
  125.      * Converts a number to its roman numeral representation
  126.      *
  127.      * @param  integer $num         An integer between 0 and 3999
  128.      *                              inclusive that should be converted
  129.      *                              to a roman numeral integers higher than
  130.      *                              3999 are supported from version 0.1.2
  131.      *           Note:
  132.      *           For an accurate result the integer shouldn't be higher
  133.      *           than 5 999 999. Higher integers are still converted but
  134.      *           they do not reflect an historically correct Roman Numeral.
  135.      *
  136.      * @param  bool    $uppercase   Uppercase output: default true
  137.      *
  138.      * @param  bool    $html        Enable html overscore required for
  139.      *                              integers over 3999. default true
  140.      * @return string  $roman The corresponding roman numeral
  141.      *
  142.      * @access public
  143.      */
  144.     function toNumeral($num, $uppercase = true, $html = true)
  145.     {
  146.         $conv = array(10 => array('X', 'C', 'M'),
  147.         5 => array('V', 'L', 'D'),
  148.         1 => array('I', 'X', 'C'));
  149.         $roman = '';
  150.  
  151.         if ($num < 0) {
  152.             return '';
  153.         }
  154.  
  155.         $num = (int) $num;
  156.  
  157.         $digit = (int) ($num / 1000);
  158.         $num -= $digit * 1000;
  159.         while ($digit > 0) {
  160.             $roman .= 'M';
  161.             $digit--;
  162.         }
  163.  
  164.         for ($i = 2; $i >= 0; $i--) {
  165.             $power = pow(10, $i);
  166.             $digit = (int) ($num / $power);
  167.             $num -= $digit * $power;
  168.  
  169.             if (($digit == 9) || ($digit == 4)) {
  170.                 $roman .= $conv[1][$i] . $conv[$digit+1][$i];
  171.             } else {
  172.                 if ($digit >= 5) {
  173.                     $roman .= $conv[5][$i];
  174.                     $digit -= 5;
  175.                 }
  176.  
  177.                 while ($digit > 0) {
  178.                     $roman .= $conv[1][$i];
  179.                     $digit--;
  180.                 }
  181.             }
  182.         }
  183.  
  184.         /*
  185.          * Preparing the conversion of big integers over 3999.
  186.          * One of the systems used by the Romans  to represent 4000 and
  187.          * bigger numbers was to add an overscore on the numerals.
  188.          * Because of the non ansi equivalent if the html output option
  189.          * is true we will return the overline in the html code if false
  190.          * we will return a _ to represent the overscore to convert from
  191.          * numeral to arabic we will always expect the _ as a
  192.          * representation of the html overscore.
  193.          */
  194.         if ($html == true) {
  195.             $over = '<span style="text-decoration:overline;">';
  196.             $overe = '</span>';
  197.         } elseif ($html == false) {
  198.             $over = '_';
  199.             $overe = '';
  200.         }
  201.  
  202.         /*
  203.          * Replacing the previously produced multiple MM with the
  204.          * relevant numeral e.g. for 1 000 000 the roman numeral is _M
  205.          * (overscore on the M) for 900 000 is _C_M (overscore on both
  206.          * the C and the M) We initially set the replace to AFS which
  207.          * will be later replaced with the M.
  208.          *
  209.          * 500 000 is   _D (overscore D) in Roman Numeral
  210.          * 400 000 is _C_D (overscore on both C and D) in Roman Numeral
  211.          * 100 000 is   _C (overscore C) in Roman Numeral
  212.          *  90 000 is _X_C (overscore on both X and C) in Roman Numeral
  213.          *  50 000 is   _L (overscore L) in Roman Numeral
  214.          *  40 000 is _X_L (overscore on both X and L) in Roman Numeral
  215.          *  10 000 is   _X (overscore X) in Roman Numeral
  216.          *   5 000 is   _V (overscore V) in Roman Numeral
  217.          *   4 000 is M _V (overscore on the V only) in Roman Numeral
  218.          *
  219.          * For an accurate result the integer shouldn't be higher then
  220.          * 5 999 999. Higher integers are still converted but they do not
  221.          * reflect an historically correct Roman Numeral.
  222.          */
  223.         $roman = str_replace(str_repeat('M', 1000),
  224.                              $over.'AFS'.$overe, $roman);
  225.         $roman = str_replace(str_repeat('M', 900),
  226.                              $over.'C'.$overe.$over.'AFS'.$overe, $roman);
  227.         $roman = str_replace(str_repeat('M', 500),
  228.                              $over.'D'.$overe, $roman);
  229.         $roman = str_replace(str_repeat('M', 400),
  230.                              $over.'C'.$overe.$over.'D'.$overe, $roman);
  231.         $roman = str_replace(str_repeat('M', 100),
  232.                              $over.'C'.$overe, $roman);
  233.         $roman = str_replace(str_repeat('M', 90),
  234.                              $over.'X'.$overe.$over.'C'.$overe, $roman);
  235.         $roman = str_replace(str_repeat('M', 50),
  236.                              $over.'L'.$overe, $roman);
  237.         $roman = str_replace(str_repeat('M', 40),
  238.                              $over.'X'.$overe.$over.'L'.$overe, $roman);
  239.         $roman = str_replace(str_repeat('M', 10),
  240.                              $over.'X'.$overe, $roman);
  241.         $roman = str_replace(str_repeat('M', 5),
  242.                              $over.'V'.$overe, $roman);
  243.         $roman = str_replace(str_repeat('M', 4),
  244.                              'M'.$over.'V'.$overe, $roman);
  245.  
  246.         /*
  247.          * Replacing AFS with M used in both 1 000 000
  248.          * and 900 000
  249.          */
  250.         $roman = str_replace('AFS', 'M', $roman);
  251.  
  252.         /*
  253.          * Checking for lowercase output
  254.          */
  255.         if ($uppercase == false) {
  256.             $roman = strtolower($roman);
  257.         }
  258.  
  259.         return $roman;
  260.     }
  261.  
  262.     // }}}
  263.  
  264. }
  265.  
  266. // }}}
  267.  
  268. /*
  269.  * Local variables:
  270.  * tab-width: 4
  271.  * c-basic-offset: 4
  272.  * End:
  273.  */
  274.  
  275. ?>
  276.